const router = require('koa-router')()
const cc = require('../conCtl')
const js = require('../jsCtl')
const db = require('../dbCtl')
const got = require('got')
const { wechat } = require('../config.json');
const wc = require('../wechatCtl.js');

var AsyncLock = require('async-lock');
var lock = new AsyncLock();


//珠珠币充值
router.post('/recharge', async c => {
    js.check_params(
        !c.request.body.money_id && '!充值项目')

    let res = {};
    let money = await db.model('t_money_set').where('money_id=?', c.request.body.money_id).shift();

    res.openid = c.request.body.openid;
    res.total_fee = money.money;
    res.body = c.request.body.body || '盘珠珠充值';
    res.attach = '珠珠币充值|' + money.money_id;
    cc.send(c, await init_trade(c, res));
});

//支付
router.post('/new', async c => {
    js.check_params(
        !c.request.body.total_fee && '!订单金额' ||
        !c.request.body.order_id && '!订单')

    let res = {};

    res.openid = c.request.body.openid;
    res.total_fee = c.request.body.total_fee;
    res.body = c.request.body.body || '盘珠珠支付';
    res.attach = c.request.body.attach || '支付';
    let result = await init_trade(c, res);
    await db.model('t_order').where('order_id=?', c.request.body.order_id).update({ trade_no: result.trade_no });
    cc.send(c, result);
});

//缴纳保证金
router.post('/bond', async c => {
    js.check_params(
        !c.request.body.auction_goods_id && '!竞拍商品')

    let res = {};

    res.openid = c.request.body.openid;

    res.total_fee = await db.model('t_auction_goods').where('auction_goods_id=?', c.request.body.auction_goods_id).shift('', 'security') * 10;
    res.body = c.request.body.body || '盘珠珠支付';
    res.attach = c.request.body.attach || '缴纳保证金';
    let result = await init_trade(c, res);
    await db.model('t_bond').insert({ trade_no: result.trade_no, auction_goods_id: c.request.body.auction_goods_id, money: res.total_fee, openid: c.request.body.openid });
    cc.send(c, result);
});

/**
 * 订单支付
 */
const init_trade = async (c, res) => {
    return new Promise(async r => {
        let ip = c.request.ip.split(':').pop();

        await lock.acquire('create_trade:' + res.openid, async () => {
            let trade_no = Math.random().toString(36).substr(2, 15) + Date.now();

            //向微信发起交易申请
            let data = await create_trade(trade_no, res.openid, res.total_fee, res.body, res.attach, ip);

            //创建本地订单
            let prepay_id = data.wechat_result.prepay_id;
            await db.model('t_trade').insert({ openid: res.openid, trade_no, prepay_id, total_fee: res.total_fee, attach: res.attach, nonce_str: data.nonce_str });
            //('INSERT INTO `t_trade`(`openid`,`trade_no`,`prepay_id`,`total_fee`,`attach`,`nonce_str`)VALUES(?,?,?,?,?,?)', [openid, trade_no, prepay_id, total_fee, attach, data.nonce_str], '创建订单失败');
            r({
                trade_no,
                h5data: data.h5data
            });
        });

    })
}

//检查订单是否支付成功
router.get('/:trade_no', async c => {
    await lock.acquire('check_trade:' + trade_no, async () => {
        //查询订单是否存在, 是否已完成
        let select_trade = await db.model('t_trade').field('`state`,`wechat_err`').where('trade_no=?', trade_no).shift('未找到订单');
        if (select_trade.state == 1) { //订单已经完成
            cc.send(c);
            return;
        } else if (select_trade.state == -1) {
            js.throwObj(select_trade.wechat_err);
            return;
        }

        //向微信求证订单
        let wechat_result = await check_trade(trade_no);
        if (wechat_result.code == -1) //微信告知交易失败,更新数据库订单
            db.start('UPDATE `t_trade` SET `state`=-1,`wechat_err`=? WHERE `trade_no`=?', [err, trade_no]);
        if (wechat_result.code <= 0) //等待用户支付
            js.throwObj(wechat_result.msg);

        //交易完成,保存交易信息
        let data = wechat_result.data;
        await db.startTransaction(async (tran) => {
            await tran.start('UPDATE `t_trade` SET `openid`=?, `wechat_no`=?,`total_fee`=?, `state`=1 WHERE `trade_no`=?', [data.openid, data.transaction_id, data.cash_fee, trade_no]);
            if (data.attach == '支付') {
                await tran.start('UPDATE `t_order` SET `state`=2 WHERE `trade_no`=?', [trade_no])
            }
            else if (data.attach.indexOf('珠珠币充值') == 0) {
                let money_id = data.attach.split('|')[1];
                if (!money_id) {
                    js.throwObj('充值失败');
                }
                let score = await tran.model('t_money_set').where('money_id=?', money_id).shift('', 'score');
                await tran.model('t_users').where('openid=?', data.openid).updateSql('points=points+?', score);
            } else if (data.attach == '缴纳保证金') {
                await tran.model('t_bond').where('trade_no=?', trade_no).update({ state: 1 });
            }
        });

        c.send(c);
    });
});



//微信通知支付结果
//xml格式的post支持
const xml2js = require('xml2js');
const json2xml = new xml2js.Builder(); //json -> xml ; json2xml.buildObject(json);
const getRawBody = require('raw-body')
router.post('/notify', async c => {
    let trade_no = '';

    try {
        // cc.log('收到一个微信通知', c.params);

        //定义一个正常回复
        let res_success = json2xml.buildObject({
            return_code: 'SUCCESS',
            return_msg: 'OK'
        });
        var resultXml = await getRawBody(c.req, {
            length: c.length,
            limit: '1mb',
            encoding: c.charset
        })
        //解析返回内容
        let resultJson = await cc.promisify(xml2js.parseString)(resultXml, {
            explicitArray: false,
            ignoreAttrs: true
        });


        //初验证订单
        let result = await notify(resultJson);
        if (result.code == 0) {//订单尚未支付完成
            cc.log('订单初验 - 尚未支付,返回success');
            cc.send(c);
            return;
        }
        let data = result.data;
        trade_no = data.out_trade_no;

        //微信通知-订单支付失败
        if (result.code == -1) {
            cc.log('订单初验 - 支付失败,更新数据库,返回success');
            db.start('UPDATE `t_trade` SET `state`=-1,`wechat_err`=? WHERE `trade_no`=?', [result.err, trade_no]);
            cc.send(c);
            return;
        }

        //数据库查询一个结果
        let select_trade = await db.start('SELECT * FROM `t_trade` WHERE `trade_no`=?', [trade_no]);
        if (!select_trade.length) throw '未找到订单'
        select_trade = select_trade[0];

        //订单签名验证
        await notify_checkSign(resultJson, select_trade);

        //微信通知-订单支付成功
        if (select_trade.state == 0) {
            await db.startTransaction(async (tran) => {
                await tran.start('UPDATE `t_trade` SET `wechat_no`=?, `state`=1 WHERE `trade_no`=?', [data.transaction_id, trade_no]);
                if (data.attach == '支付') {
                    await tran.start('UPDATE `t_order` SET `state`=2 WHERE `trade_no`=?', [trade_no])
                }
                else if (data.attach.indexOf('珠珠币充值') == 0) {
                    let money_id = data.attach.split('|')[1];
                    if (money_id) {
                        let score = await tran.model('t_money_set').where('money_id=?', money_id).shift('', 'score');
                        await tran.model('t_users').where('openid=?', data.openid).updateSql('points=points+?', score);
                    }
                } else if (data.attach == '缴纳保证金') {
                    await tran.model('t_bond').where('trade_no=?', trade_no).update({ state: 1 });
                }
            });

            //获取邀请参数
            let setting = await db.model('t_invitation').shift();
            //判断客户是否第一次下单
            let is_frist = await db.model('t_users').where('openid=?', data.openid).shift('', 'is_frist');
            if (is_frist == 0) {
                let pid_openid = await db.model('t_user_share').where('openid=?', data.openid).shift('', 'pid_openid');
                if (pid_openid) {
                    await db.model('t_users').where('openid=?', data.openid).updateSql('points=points+?,lottery_ticket=lottery_ticket+?,is_frist=1', setting.uscore, setting.uticket);//被邀请者奖励
                    await db.model('t_users').where('openid=?', pid_openid).updateSql('points=points+?,lottery_ticket=lottery_ticket+?', setting.iscore, setting.iticket);//被邀请者奖励
                } else {
                    await db.model('t_users').where('openid=?', data.openid).update({is_frist:1});//修改用户是否第一次下单
                }
            }
        }

        cc.log('微信通知 - 交易成功' + trade_no);
        c.body = res_success;
    } catch (err) {
        cc.log('微信通知 - 处理失败' + trade_no, err);
        let res_fail = json2xml.buildObject({
            return_code: 'FAIL',
            return_msg: err
        });
        c.body = res_fail;
    }
});





//--------------------------------------------------------------------------




const crypto = require('crypto');
const queryString = require('querystring');
const { pid } = require('process')




/**
 * 生成微信支付订单内容
 */
const create_trade = async (out_trade_no, openid, total_fee, body, attach, ip) => {
    //测试ip
    // ip = '123.123.123.13';
    // 返回 promise 对象
    return new Promise(async (r, e) => {
        let data = {
            openid: openid,
            appid: wechat.appId,
            attach: attach,
            body: body,
            mch_id: wechat.pay.mch_id,
            nonce_str: Math.random().toString(36).substr(2, 15),
            notify_url: wechat.base.baseurl + wechat.base.back_url, // 微信付款后的回调地址
            timeStamp: parseInt(Date.now() / 1000) + '',
            out_trade_no: out_trade_no, //订单号
            spbill_create_ip: ip,
            total_fee: total_fee,
            trade_type: 'JSAPI',
        };
        data.sign = wc.getSign(data);
        let dataxml = json2xml.buildObject(data);

        let got_result = await got("https://api.mch.weixin.qq.com/pay/unifiedorder", {
            method: 'POST', //post请求
            body: dataxml
        });

        //解析返回内容
        let result = await cc.promisify(xml2js.parseString)(got_result.body, {
            explicitArray: false,
            ignoreAttrs: true
        });

        let datajson = result.xml;
        if (datajson.return_code != "SUCCESS") {
            e(datajson.return_msg);
            return;
        } else if (datajson.result_code != "SUCCESS") {
            e(datajson.err_code_des);
            return;
        }

        data.wechat_result = datajson;
        //生成手机端唤起支付的内容
        data.h5data = {
            appId: data.appid,
            timeStamp: data.timeStamp,
            nonceStr: data.nonce_str,
            package: 'prepay_id=' + datajson.prepay_id,
            signType: 'MD5',
        }
        data.h5data.paySign = wc.getSign(data.h5data);
        r(data);
    })
}
/**
 * 向微信查询交易订单情况
 */
const check_trade = async (trade_no) => {
    return new Promise(async (r, e) => {

        //构建查询内容
        let data = {
            appid: wechat.appId,
            mch_id: wechat.pay.mch_id,
            out_trade_no: trade_no,
            nonce_str: Math.random().toString(36).substr(2, 15),
            sign_type: 'MD5'
        }
        data.sign = wc.getSign(data);
        let dataxml = json2xml.buildObject(data);

        //********向微信查证订单交易情况
        let got_result = await got("https://api.mch.weixin.qq.com/pay/orderquery", {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: dataxml
        });

        //解析返回内容
        let result = await cc.promisify(xml2js.parseString)(got_result.body, {
            explicitArray: false,
            ignoreAttrs: true
        });
        let datajson = result.xml;

        let err_msg = '';
        if (datajson.return_code != "SUCCESS") {
            err_msg = datajson.return_msg;
        } else if (datajson.result_code != "SUCCESS") {
            err_msg = datajson.err_code_des;
        }
        //订单状态不是这三种，则认为是错误
        else if (
            datajson.trade_state != "SUCCESS" && //—支付成功
            datajson.trade_state != "NOTPAY" && //—未支付
            datajson.trade_state != "USERPAYING" //-用户支付中
        ) {
            err_msg = trade_state;
        }
        //查询结果为失败
        if (err_msg) { //若返回了订单号，则认为交易失败
            r({ code: -1, msg: '微信交易失败,' + err_msg });
        }
        //NOTPAY或USERPAYING状态，直接返回，等待下一次查询
        else if (datajson.trade_state != "SUCCESS") {
            r({ code: 0, msg: '尚未支付成功,请支付后重试!' });
        }
        //********向微信查证订单交易情况结束,微信结果为已交易成功
        else {
            r({ code: 1, data: datajson });
        }

    }) //promise end
}

const notify = async (wechat_body) => {
    return new Promise((r) => {

        if (!wechat_body.xml) {
            e('微信通知格式错误');
            return;
        }

        let datajson = wechat_body.xml;

        let err_msg = '';
        if (datajson.return_code != "SUCCESS") {
            err_msg = datajson.return_msg;
        } else if (datajson.result_code != "SUCCESS") {
            err_msg = datajson.err_code_des;
        }
        //查询结果为失败
        if (err_msg) { //若返回了订单号，则认为交易失败
            r({ code: -1, msg: '微信交易失败,' + err_msg, data: datajson });
            return;
        } else {
            r({ code: 1, data: datajson });
        }


    })//promise end
}
const notify_checkSign = async (wechat_body, trade_data) => {
    return new Promise((r) => {

        let datajson = wechat_body.xml;

        let sign1 = wc.getSign({
            appid: wechat.appId,
            attach: trade_data.attach,
            bank_type: datajson.bank_type,
            cash_fee: datajson.cash_fee,
            fee_type: datajson.fee_type,
            is_subscribe: datajson.is_subscribe,
            mch_id: wechat.pay.mch_id,
            nonce_str: trade_data.nonce_str,
            openid: trade_data.openid,
            out_trade_no: trade_data.trade_no, //订单号
            result_code: datajson.result_code,
            return_code: datajson.return_code,
            time_end: datajson.time_end,
            total_fee: trade_data.total_fee,
            trade_type: 'JSAPI',
            transaction_id: datajson.transaction_id
        });
        if (sign1 != datajson.sign) {
            e('签名验证失败');
        }

        r();

    })//promise end
}

module.exports = router;